package com.nisovin.magicspells.spells.buff;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryType.SlotType;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.ItemMeta;

import com.nisovin.magicspells.MagicSpells;
import com.nisovin.magicspells.spells.BuffSpell;
import com.nisovin.magicspells.util.MagicConfig;
import com.nisovin.magicspells.util.Util;

public class ArmorSpell extends BuffSpell {

	private boolean permanent;
	private boolean replace;
	
	private ItemStack helmet;
	private ItemStack chestplate;
	private ItemStack leggings;
	private ItemStack boots;
	
	private String strHasArmor;
	private String strLoreText;
	
	private Set<String> armored;
	
	public ArmorSpell(MagicConfig config, String spellName) {
		super(config, spellName);
		
		permanent = getConfigBoolean("permanent", false);
		replace = getConfigBoolean("replace", false);
		
		strLoreText = ChatColor.translateAlternateColorCodes('&', getConfigString("str-lore-text", "Conjured"));
		
		helmet = getItem(getConfigString("helmet", ""));
		chestplate = getItem(getConfigString("chestplate", ""));
		leggings = getItem(getConfigString("leggings", ""));
		boots = getItem(getConfigString("boots", ""));
		
		strHasArmor = getConfigString("str-has-armor", "You cannot cast this spell if you are wearing armor.");
		
		armored = new HashSet<String>();
	}
	
	@Override
	public void initialize() {
		super.initialize();
		if (!permanent) {
			registerEvents(new ArmorListener());
		}
	}
	
	private ItemStack getItem(String s) {
		if (!s.isEmpty()) {
			String[] info = s.split(" ");
			try {
				
				// get type and data
				ItemStack item = Util.getItemStackFromString(info[0]);
				if (item == null) return null;
				item.setAmount(1);
				if (!permanent) {
					ItemMeta meta = item.getItemMeta();
					List<String> lore;
					if (meta.hasLore()) {
						lore = meta.getLore();
					} else {
						lore = new ArrayList<String>();
					}
					lore.add(strLoreText);
					meta.setLore(lore);
					item.setItemMeta(meta);
				}
				
				// get enchantments (left for backwards compatibility)
				if (info.length > 1) {
					for (int i = 1; i < info.length; i++) {
						String[] enchinfo = info[i].split(":");
						Enchantment ench = Util.getEnchantmentType(enchinfo[0]);
						int lvl = 1;
						if (enchinfo.length > 1) {
							lvl = Integer.parseInt(enchinfo[1].toUpperCase().replace(" ", "_"));
						}
						if (ench != null) {
							item.addUnsafeEnchantment(ench, lvl);
						}
					}
				}
				
				return item;
			} catch (NumberFormatException e) {
				return null;
			}
		} else {
			return null;
		}
	}

	@Override
	public boolean castBuff(Player player, float power, String[] args) {
		PlayerInventory inv = player.getInventory();
		if (!replace && ((helmet != null && inv.getHelmet() != null) || (chestplate != null && inv.getChestplate() != null) || (leggings != null && inv.getLeggings() != null) || (boots != null && inv.getBoots() != null))) {
			// error
			sendMessage(player, strHasArmor);
			return false;
		}
		
		setArmor(inv);
		
		if (!permanent) {
			armored.add(player.getName());
		}
		return true;
	}
	
	private void setArmor(PlayerInventory inv) {
		if (helmet != null) {
			inv.setHelmet(helmet.clone());
		}
		if (chestplate != null) {
			inv.setChestplate(chestplate.clone());
		}
		if (leggings != null) {
			inv.setLeggings(leggings.clone());
		}
		if (boots != null) {
			inv.setBoots(boots.clone());
		}
	}
	
	private void removeArmor(PlayerInventory inv) {
		if (helmet != null && inv.getHelmet() != null && inv.getHelmet().getType() == helmet.getType()) {
			inv.setHelmet(null);
		}
		if (chestplate != null && inv.getChestplate() != null && inv.getChestplate().getType() == chestplate.getType()) {
			inv.setChestplate(null);
		}
		if (leggings != null && inv.getLeggings() != null && inv.getLeggings().getType() == leggings.getType()) {
			inv.setLeggings(null);
		}
		if (boots != null && inv.getBoots() != null && inv.getBoots().getType() == boots.getType()) {
			inv.setBoots(null);
		}
	}
	
	class ArmorListener implements Listener {
	
		@EventHandler(priority=EventPriority.MONITOR, ignoreCancelled=true)
		public void onEntityDamage(EntityDamageEvent event) {
			if (event.getEntity() instanceof Player) {
				Player p = (Player)event.getEntity();
				if (isActive(p) && p.getNoDamageTicks() < 10) {
					addUseAndChargeCost(p);
				}
			}
		}
		
		@EventHandler(ignoreCancelled=true)
		public void onInventoryClick(InventoryClickEvent event) {
			if (event.getSlotType() == SlotType.ARMOR && event.getWhoClicked() instanceof Player) {
				Player p = (Player)event.getWhoClicked();
				if (isActive(p)) {
					event.setCancelled(true);
				}
			}
		}
		
		@EventHandler
		public void onPlayerDeath(PlayerDeathEvent event) {
			Iterator<ItemStack> drops = event.getDrops().iterator();
			while (drops.hasNext()) {
				ItemStack drop = drops.next();
				List<String> lore = drop.getItemMeta().getLore();
				if (lore != null && lore.size() > 0 && lore.get(lore.size()-1).equals(strLoreText)) {
					drops.remove();
				}
			}
		}
		
		@EventHandler
		public void onPlayerRespawn(PlayerRespawnEvent event) {
			if (isActive(event.getPlayer()) && !isExpired(event.getPlayer())) {
				final PlayerInventory inv = event.getPlayer().getInventory();
				Bukkit.getScheduler().scheduleSyncDelayedTask(MagicSpells.plugin, new Runnable() {
					public void run() {
						setArmor(inv);
					}
				});
			}
		}
		
		@EventHandler
		public void onPlayerQuit(PlayerQuitEvent event) {
			if (isActive(event.getPlayer())) {
				if (cancelOnLogout) {
					turnOff(event.getPlayer());
				} else {
					removeArmor(event.getPlayer().getInventory());
				}
			}
		}
		
		@EventHandler
		public void onPlayerJoin(PlayerJoinEvent event) {
			if (isActive(event.getPlayer())) {
				if (!isExpired(event.getPlayer())) {
					setArmor(event.getPlayer().getInventory());
				} else {
					turnOff(event.getPlayer());
				}
			}
		}
	}
	
	@Override
	public void turnOffBuff(Player player) {
		if (armored.remove(player.getName())) {
			PlayerInventory inv = player.getInventory();
			removeArmor(inv);
		}
	}

	@Override
	protected void turnOff() {
		for (String name : new HashSet<String>(armored)) {
			Player p = Bukkit.getPlayerExact(name);
			if (p != null && p.isOnline()) {
				turnOff(p);
			}
		}
	}

	@Override
	public boolean isActive(Player player) {
		return armored.contains(player.getName());
	}

}
